package org.javahispano.javacup.tacticas_aceptadas.jpautom.org.javahispano.javacup.tacticas.pitufos;

import java.util.Arrays;
import org.javahispano.javacup.modelo.Constantes;

public class Tiro extends MiniTactica{

	public void ejecutar() {
		Campo c=(Campo)getCampo().clone();
		for (int i=0; i<11; i++) {
			if (Util.tieneBola(c, i)) {
				//tiroAlto(c, i);
				tiro(c, i);
			}
		}
	}

	private void tiroAlto(Campo c, int j) {
		double dist=c.getBola().getPos().distance2d(0, Constantes.LARGO_CAMPO_JUEGO/2);
		if (dist>Constantes.LARGO_CAMPO_JUEGO/3) return;

		double v_max=c.getInfo().getF(j);
		double ang_max=Constantes.ANGULO_VERTICAL_MAX*Math.PI/180;
		int gArco=(int)Math.round(Constantes.ALTO_ARCO/Constantes.G);
		int gVz_min=(int)Math.ceil((-1+Math.sqrt(1+8*gArco))/2);
		//int gVz_max=(int)Math.round(v_max*Math.sin(ang_max)/Constantes.G);
		int gVz_max=(int)Math.round((v_max/2)/Constantes.G);

		for (int gVz=gVz_min; gVz<=gVz_max; gVz++) {
			int t_arco=(int)Math.ceil(((2*gVz+1)+Math.sqrt((2*gVz+1)*(2*gVz+1)-8*gArco))/2);
			//double Vxy_min=Constantes.G*(gVz-0.5)/Math.tan(ang_max);
			//double Vxy_max=Math.sqrt(v_max*v_max-Math.pow(Constantes.G*(gVz-0.5), 2));
			double vz=Constantes.G*(gVz-0.5);
			double th_min=Math.asin(2*(gVz-0.5)*Constantes.G/v_max)/2;
			double th_max=Math.min(Math.PI/2-th_min, ang_max);
			double Vxy_min=vz/Math.sin(th_max); //todo: revisar si el minimo se alcanza con gVz-0.5
			double Vxy_max=vz/Math.sin(th_min); //todo: revisar si el maximo se alcanza con gVz-0.5
			if (Double.isNaN(Vxy_max)) Vxy_max=v_max;
			double vxy=dist*(1-Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE)/(1-Math.pow(Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE, t_arco));
			if (vxy>Vxy_min && vxy<Vxy_max) {
				//double fz=Math.sqrt(Math.pow(Constantes.G*(gVz-0.5), 2)+vxy*vxy);
				//double th=Math.acos(vxy/fz);
				double th=Math.asin(vz/vxy);
				double fz=vxy/Math.cos(th);
				double phi=Util.angulo(c.getBola().getPos(), new Pos(0.0, Constantes.LARGO_CAMPO_JUEGO/2, 0.0));
				comandos.add(Util.crearComandoGolpearBalon(c, j, fz, th, phi));
				return;
			}
		}
	}
/*
	private void punterazo(Campo c, int j) {
		Pos b=c.getBola().getPos();
		double vxy=c.getInfo().getF(j);
		double d1=b.distance(poste1);
		double d2=b.distance(poste2);

		int t1=(int)TparaDxyKs(vxy, d1);
		int t2=(int)TparaDxyKs(vxy, d2);
		if (t1<=0 || t2<=0) return;

		double a1=Util.angulo(b, poste1);
		double a2=Util.angulo(b, poste2);





	}
*/

	private void tiro(Campo c, int j) {
		double phi1=Util.angulo(c.getBola().pos, poste1);
		double phi2=Util.angulo(c.getBola().pos, poste2);
		double v_max=c.getInfo().getF(j);
		double ang_max=Constantes.ANGULO_VERTICAL_MAX*Math.PI/180;
		double umbral_vxy_min=v_max/2;
		
		int NGRANOPHI=15;
		double DELTAVXY=0.2;

		int iError=(int)Math.abs(c.getInfo().getP(j)/((phi2-phi1)/NGRANOPHI));
		int[] exito=new int[NGRANOPHI];
		double maxExito=0.0;
		double mejorVz=0.0;
		double mejorVxy=0.0;
		double mejorPhi=0.0;

		int gvz_max=(int)Math.round(v_max/(2*G));
		for (int gvz=0; gvz<=gvz_max; gvz++) {
			double th_min=Math.asin(2*gvz*G/v_max)/2;
			double th_max=Math.min(ang_max, Math.PI/2-th_min);
			double vxy_min=gvz*G/Math.sin(th_max);
			double vxy_max=gvz*G/Math.sin(th_min);
			if (Double.isNaN(vxy_max)) vxy_max=v_max;
			if (vxy_min<umbral_vxy_min) vxy_min=umbral_vxy_min;

			for (double vxy=vxy_max; vxy>=vxy_min; vxy-=DELTAVXY) {
				for (int iphi=0; iphi<NGRANOPHI; iphi++) {
					double phi=phi1+iphi*(phi2-phi1)/(NGRANOPHI-1);
					// Comprobación
					Util.crearComandoGolpearBalonZ(c, j, vxy, gvz*G, phi);


					c.getBola().mov.setVxyz(vxy*Math.cos(phi), vxy*Math.sin(phi), gvz*G);
					if (seraGol(c)) exito[iphi]=1;
					else exito[iphi]=0;
				}

				int i;
				int tot=0;
				for (i=0; i<Math.min(iError, exito.length); i++) {
					tot+=exito[i];
				}
				if ((double)tot/iError>maxExito) {
					maxExito=(double)tot/iError;
					mejorPhi=phi1+((phi2-phi1)/(NGRANOPHI-1))*i/2;
					mejorVxy=vxy;
					mejorVz=gvz*G;
				}

				for (i++; i<exito.length; i++) {
					tot=tot-exito[i-iError]+exito[i];
					if ((double)tot/iError>maxExito) {
						maxExito=(double)tot/iError;
						mejorPhi=phi1+((phi2-phi1)/(NGRANOPHI-1))*i/2;
						mejorVxy=vxy;
						mejorVz=gvz*G;
					}
				}
			}
		}
		Util.log("maxExito=%f\n", maxExito);
		if (maxExito>0.35) comandos.add(Util.crearComandoGolpearBalonZ(c, j, mejorVxy, mejorVz*G, mejorPhi));
	}

	private boolean seraGol(Campo c) {
		Campo.Bola b=(Campo.Bola)c.getBola().clone();
		// Comprueba que el tiro al menos llega a línea de fondo
		double dmax=0.0;
		int gVz=(int)Math.round(b.getMov().getVz()/Constantes.G);
		double vxy=b.getMov().getVxy();
		int t=0;
		while (gVz>3) {
			t+=2*gVz+1;
			gVz=(int)Math.round(gVz*Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE);
		}
		dmax+=vxy*(1-Math.pow(Ka, t))/(1-Ka);
		vxy*=Math.pow(Ka, t);
		dmax+=vxy/(1-Ks);
		if (dmax < (Constantes.LARGO_CAMPO_JUEGO/2-b.getPos().y)/Math.sin(b.getMov().getPhi())) {
			// bola no llega ni a linea de fondo
			return false;
		}

		Pos pOld=new Pos();
		for (t=1; Util.dentroCampo(b.getPos()); t++) {
			pOld.set(b.getPos());
			Util.futuroBola(b, 1);
			for (int j=11; j<22; j++) {
				if (c.getJugador(j).pos.distance2d(b.pos)<=t*c.getInfo().getV(j)+Util.getAlcanceH(c, j, b.pos))
					return false;
			}
		}

		if (b.pos.z>Constantes.ALTO_ARCO) return false;
		return true;

	}




	private static final double Ks=Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
	private static final double Ka=Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
	private static final Pos poste1=new Pos(Constantes.LARGO_ARCO/2-Constantes.RADIO_BALON, Constantes.LARGO_CAMPO_JUEGO/2, 0);
	private static final Pos poste2=new Pos(-Constantes.LARGO_ARCO/2+Constantes.RADIO_BALON, Constantes.LARGO_CAMPO_JUEGO/2, 0);
	private static final double G=Constantes.G;


	private double TparaDxyKs(double vxy, double dxy) {
		if (dxy>vxy/(1-Ks)) return -1.0;
		return Math.log(1-(1-Ks)*dxy/vxy);
	}

	private double DxyparaTKs(double vxy, double t) {
		return vxy*(1-Math.pow(Ks, t))/(1-Ks);
	}
}
